package com.lyz.file.impl;

import java.io.File;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;

import org.apache.log4j.Logger;

import com.lyz.config.ConfigFactory;
import com.lyz.config.IConfig;
import com.lyz.file.IFileHandler;
import com.lyz.file.IFileProcess;
import com.lyz.file.IFileQueue;
import com.lyz.util.BaseTypeUtil;
import com.lyz.util.FileUtil;
import com.lyz.util.PropertiesUtil;
import com.lyz.util.SleepUtil;

/**
 * 文件处理类，读取文件内容，并记录文件进度
 * 
 * @author luyongzhao
 * 
 */
public class LogFileHandler implements IFileHandler {

	private final static Logger logger = Logger.getLogger(LogFileHandler.class);

	private static LogFileHandler handler = null;

	private volatile long position = 0;
	/**
	 * wait time for read new line
	 */
	private int waitTime = 0;

	private RandomAccessFileExt raf = null;

	private File file = null;

	private IFileQueue queue = null;

	private IConfig config = null;

	private IFileProcess proc = null;
	
	private String tag = null;
	
	private byte[] byteBuffer = null;
	
	private String defaultEncode = "utf-8";

	/************************** for stastistic ********************/
//	private long startTime = 0;
//	private long endTime = 0;
//	// private long lineCount = 0;
//	private long totalWaitTime = 0;

	public LogFileHandler(IFileQueue queue, IFileProcess proc,String tag) {
		this.tag = tag;
//		startTime = System.currentTimeMillis();
		config = ConfigFactory.getConfig();
		defaultEncode = config.getEncode();
		/*初始化文件进度*/
		this.proc = proc;
		/*初始化日志文件队列*/
		this.queue = queue;
		/*从队列中获取一个文件*/
		file = this.createNewFile();
		/*初始化字节缓存大小*/
		byteBuffer = new byte[config.getBytesBufferSize()];

		logger.info("startFile:" + file.getName() + " position=" + position);
		try {
			// "rd" is a invalid mode
			raf = new RandomAccessFileExt(file, "rwd");
			raf.seek(position);
			logger.info("find new file position...");
		} catch (FileNotFoundException e) {
			logger.error("can not find file " + file.getName(), e);
		} catch (IOException e) {
			logger.error("error in creating GPSFileHandler", e);
		}
	}

	/**
	 * get existing file from queue
	 * 
	 * @return
	 */
	private File createNewFile() {

		// for stastistic
//		endTime = System.currentTimeMillis();
		// lineCount = 0;
//		startTime = endTime;
//		totalWaitTime = 0;

		String fileName = null;
		File file = null;
		/*获取一个存在的文件*/
		String fileFullPath = null;
		do {
			fileName = queue.getNextFile();
//			file = new File(proUtil.getLogDirectory(tag) + "/" + fileName);
			/*根据文件名称推测文件夹名称*/
			fileFullPath = getRightFileDir(fileName) + "/" + fileName;
			file = new File(fileFullPath);
			logger.info("try to handle file:" + fileFullPath);
		} while (!file.exists());
		/*如果没有进度文件（初始化运行），或者进度文件不是当前的文件，则进行替换*/
		if ("".equals(proc.getNextFile()) || (!fileName.equals(proc.getNextFile())) ) {
			proc.updateNextFile(fileName);
			position = 0;
			logger.info("start to handle file:" + fileFullPath);
		} else {
			position = proc.getNextPosition();
		}

		return file;
	}
	/**
	 * 针对一些带有动态时间的文件夹，跨天获取会出现取不到文件的状况，需要根据文件名称推测文件文件夹名称
	 * 比如：本身文件是20141107/fileName,但处理该文件时已经是20141108，则
	 * @param fileDir
	 * @param fileName
	 * @return
	 */
	private String getRightFileDir(String fileName){
		
		String dateFormat = config.getDateFormat(tag);
		String time = BaseTypeUtil.getSubString(fileName, 
				config.getFileStart(tag)+config.getFileSplit(tag), config.getFileEndWith(tag));
		Date date = new Date(BaseTypeUtil.getFormatTime(dateFormat, time));
		
		return config.getLogDirectory(tag, date);
	}


	@Override
	public String readLine() {

		String line = null;

		do {
			line = readLine0();
		} while (line == null);

		// lineCount++;
		return line;

	}

	private String getNewLine() {
		String line = null;
		try {
			line = raf.readLine(defaultEncode);
			position = raf.getFilePointer();
			/*更新进度位置*/
			proc.updateNextPosition(position);

		} catch (IOException e1) {
			logger.error("error to read new line!", e1);
			return line;
		}
		return line;
	}

	/**
	 * read files to get new line. If no new line, it will be blocked
	 * 
	 * @return
	 */
	private String readLine0() {

		String line = getNewLine();
		if (line == null) {
			// if queue is not empty, close old file and open new file
			if (!queue.isEmpty()) {
				// position = 0;
				try {
					raf.close();
					file = createNewFile();
					raf = new RandomAccessFileExt(file, "rwd");
					line = getNewLine();
				} catch (FileNotFoundException e) {
					logger.error("error to find new file", e);
				} catch (IOException e) {
					logger.error("error to close or read new line", e);
				}
			} else {
				// wait for new line append to file for a certain time
				while (line == null) {

					try {
						raf.close();
					} catch (IOException e) {
						logger.error("error to close file", e);
					}

					// wait for a certain time
					waitTime = SleepUtil.sleep(waitTime);
					// for stastistic
//					totalWaitTime += waitTime;

					try {
						// try to find new line
						raf = new RandomAccessFileExt(file, "rwd");
						raf.seek(position);
						line = getNewLine();
					} catch (FileNotFoundException e) {
						logger.error("error to retry to open file", e);
					} catch (IOException e) {
						logger.error(
								"error to seek or read a new line from file", e);
					}

					// timeout is 1 seconds for inner while
					if (waitTime >= SleepUtil.maxWaitTime) {
						waitTime = 0;
						break;
					}

				}// end of while
			}// end of else

		}

		waitTime = 0;
		return line;

	}

	@Override
	public void close() {

		if (raf != null) {

			try {
				raf.close();
			} catch (IOException e) {
				logger.error("error to close file", e);
			}
		}

	}

	@Override
	public long getPosition() {

		return this.position;
	}


	@Override
	public byte[] readBytes() {
		byte[] newBytes = null;
		
		do{
			newBytes = readBytes0();
		}while(newBytes==null || newBytes.length==0);
		
		return newBytes;
	}
	
	private byte[] readBytes0(){
		byte[] bytes = getNewBytes();
		if (bytes == null) {
			// if queue is not empty, close old file and open new file
			if (!queue.isEmpty()) {
				// position = 0;
				try {
					raf.close();
					file = createNewFile();
					raf = new RandomAccessFileExt(file, "rwd");
					bytes = getNewBytes();
				} catch (FileNotFoundException e) {
					logger.error("error to find new file", e);
				} catch (IOException e) {
					logger.error("error to close or read new line", e);
				}
			} else {
				// wait for new line append to file for a certain time
				while (bytes == null) {

					try {
						raf.close();
					} catch (IOException e) {
						logger.error("error to close file", e);
					}

					// wait for a certain time
					waitTime = SleepUtil.sleep(waitTime);
					// for stastistic
//					totalWaitTime += waitTime;

					try {
						// try to find new line
						raf = new RandomAccessFileExt(file, "rwd");
						raf.seek(position);
						bytes = getNewBytes();
					} catch (FileNotFoundException e) {
						logger.error("error to retry to open file", e);
					} catch (IOException e) {
						logger.error(
								"error to seek or read a new line from file", e);
					}

					// timeout is 1 seconds for inner while
					if (waitTime >= SleepUtil.maxWaitTime) {
						waitTime = 0;
						break;
					}

				}// end of while
			}// end of else

		}

		waitTime = 0;
		return bytes;
	}
	
	private byte[] getNewBytes(){
		int count = 0;
		byte[] newBytes = null;
		try {
			count = raf.read(byteBuffer);
			position = raf.getFilePointer();

			proc.updateNextPosition(position);

		} catch (IOException e1) {
			logger.error("error to read new line!", e1);
			return null;
		}
		/*复制新数据*/
		if(count>=0){
			newBytes = new byte[count];
			System.arraycopy(byteBuffer, 0, newBytes, 0, count);
		}
		
		return newBytes;
	}
	
	
	 public static void main(String args[]){
		 //System.out.println(GPSFileHandler.getInstance().getDateTimeFromFileName("gps_info_201112121905.log"));
		 //System.out.println("gps_info_201112121905.log".substring(0,"gps_info_201112121905.log".indexOf('.')).split("_")[2]);
//		 GPSFileHandler.getInstance();
			 System.out.println(Integer.toHexString(5178));
		 }

}
